/*
 * 该demo程序的主要机制是实现看门狗机制
 * 方式为看门狗服务程序充当父进程，通过exe方式启动各个子进程
 * 当子进程异常退出或被kill掉后，父进程检测到以后又给拉起来
 * 此机制的好处，是看门狗作为服务程序监控各个子进程，减少不必要的硬件重启
 * 不好的地方在于，子进程需指定，不灵活
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string>
#include <iostream>
#include <list>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

#define PRI_HEAD_WTDSERVER	"看门狗服务"

class process_list_info
{
public:
	std::string name;
	std::string param;
	int delay;
};

class process_list_cfg
{
public:
	process_list_info m_pro_list_info[3];
};

static process_list_cfg	pro_list_cfg_def = 
{
	{
		/* name				param	delay */
		{"client_first",	"",		2000},
		{"client_second",	"",		2000},
		{"client_three",	"",		2000},
	}
};

struct process_info
{
	std::string name;
	std::string param;
	pid_t pid;
	bool is_running;
	int delay;
};

class wtdserver
{
public:
	wtdserver();
	~wtdserver();

public:
	int get_process_list(void);
	int kill_all_process(void);
	int check_process_list(void);
	pid_t start_sub_process(std::string &name, std::string &param);
	void clear_sub_process(pid_t pid);

private:
	process_list_cfg	m_proc_list_cfg;
	std::list<struct process_info> m_proc_list;
};

wtdserver wtdser;

wtdserver::wtdserver()
{
}

wtdserver::~wtdserver()
{
}

int wtdserver::get_process_list(void)
{
	int i;
	struct process_info process;

	m_proc_list_cfg = pro_list_cfg_def;

	for (i=0; i<3; i++)
	{
		 if (m_proc_list_cfg.m_pro_list_info[i].name.length() > 0)
		 {
			process.name = m_proc_list_cfg.m_pro_list_info[i].name;
			process.delay = m_proc_list_cfg.m_pro_list_info[i].delay;
			process.is_running = false;
			printf("get process: %s\n", process.name.c_str());
			m_proc_list.push_back(process);
		 }
	}

	return 0;
}

int wtdserver::kill_all_process(void)
{
	char cmd[256];
	std::list<struct process_info>::iterator it;

	for (it=m_proc_list.begin(); it!=m_proc_list.end(); it++)
	{
		sprintf(cmd, "killall %s", it->name.c_str());
		system(cmd);
	}

	return 0;
}

pid_t wtdserver::start_sub_process(std::string &name, std::string &param)
{
	pid_t sub_pid = -1;
	int i = 2;
	char *argv[32];
	const char *delim = " ";	//space
	char arg_str[256];

	memcpy(arg_str, param.c_str(), 256);
	argv[0] = (char *)name.c_str();
	argv[1] = strtok(arg_str, delim);
	for (i=2; i<32; i++)
	{
		argv[i] = strtok(NULL, delim);
	}

	sub_pid = fork();
	if (sub_pid == 0)
	{
		if (execvp(argv[0], argv) == -1)
		{
			printf("execvp %s err\n", argv[0]);
		}
		/* 
		 * 如果上一条语句执行正确，那么下两条语句永远不会执行, 不管子进程是否return或exit
		 * 当子进程返回或者退出后，进程会僵死，不会退出,而且也kill或killall不掉，哪怕用-9参数，只有退出父进程才会退出
		 * 当不是后台启动父进程时，终止掉父进程，子进程会退出，如果是后台执行，则kill掉父进程，子进程不会退出
		 * 如果子进程不存在，则会产生与父进程同名的僵尸进程,而且kill不掉，加-9也kill不掉
		 */
		printf("####### return ###### start sub process: %s\n", argv[0]);
		exit(-1);
	}

	/*
	 * 返回子进程的进程号
	 */
	return sub_pid;
}

int wtdserver::check_process_list(void)
{
	std::list<struct process_info>::iterator it;

	for (it=m_proc_list.begin(); it!=m_proc_list.end(); it++)
	{
		if (!(it->is_running))
		{
			it->delay = 0;
			it->pid = start_sub_process(it->name, it->param);
			if (it->pid <= 0)
			{
				it->is_running = false;
				printf("start sub process faild\n");
			}
			else
			{
				it->is_running = true;
				printf("start sub process success, name: %s, pid: %d\n",
						it->name.c_str(), it->pid);
			}
		}
	}

	return 0;
}

void wtdserver::clear_sub_process(pid_t pid)
{
	std::list<struct process_info>::iterator it;

	for (it = m_proc_list.begin(); it!=m_proc_list.end(); it++)
	{
		if (it->pid == pid)
		{
			it->is_running = false;
			/*
			 * 一下这句是否需要还需要验证，就这样编译会出错，必须用重构方式返回一个特定类型才行
			 */
		//	m_proc_list.remove_if(it->name.c_str());
			printf("process: %s have been remove_if\n", it->name.c_str());
		}
	}
}

void on_signal_sigchld(int signo)
{
	pid_t pid;
	int status = 0;
	pid = waitpid(-1, &status, 0);
	
	wtdser.clear_sub_process(pid);
}

int main(int argc, char *argv[])
{

	printf("%s: 进程启动\n", PRI_HEAD_WTDSERVER);

	/*
	 * 以下信号，是检测子进程的信号，不论是子进程自动退出还是被kill掉，父进程都会收到此信号
	 * 此信号由系统自动发送
	 */
	signal(SIGCHLD, on_signal_sigchld);

	/*
	 * 通过配置文件或者默认文件获取必须启动哪些进程
	 */
	wtdser.get_process_list();

	/*
	 * 该函数的目的是，杀掉之前所有的程序，目的是必须让各个子进程成为
	 * 自己的子进程，不然收不到信号
	 */
	wtdser.kill_all_process();
	
	while (1)
	{
		/*
		 * 实时监测各个子进程是否正常，如不正常，重启子程序
		 */
		wtdser.check_process_list();

		sleep(5);
		printf("wtdserver sleep()\n");
	}

	return 0;
}

